home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / stdwin / Packs / textedit / textedit.c < prev    next >
Text File  |  1995-12-21  |  16KB  |  867 lines

  1. /* Text Edit, high level routines */
  2.  
  3. #include "text.h"
  4.  
  5. /* Forward */
  6. static void teinschar();
  7. static void tesetoptdata();
  8. static bool teoptinschar();
  9. static void teinsert();
  10. static void terecompute();
  11. static int tesetstart();
  12. static int teendofline();
  13. static int tewordbegin();
  14. static int tewordend();
  15. static int _wdrawpar();
  16.  
  17. void
  18. tereplace(tp, str)
  19.     TEXTEDIT *tp;
  20.     char *str;
  21. {
  22.     int len= strlen(str);
  23.     
  24.     if (len == 1 && teoptinschar(tp, (int) str[0]))
  25.         return;
  26.     
  27.     teinsert(tp, str, len);
  28. }
  29.  
  30. static void
  31. teinschar(tp, c)
  32.     TEXTEDIT *tp;
  33.     int c;
  34. {
  35.     char cbuf[2];
  36.     
  37.     if (teoptinschar(tp, c))
  38.         return;
  39.     
  40.     cbuf[0]= c;
  41.     cbuf[1]= EOS;
  42.     teinsert(tp, cbuf, 1);
  43. }
  44.  
  45. /* Interfaces for wchange and wscroll that clip to the viewing rectangle */
  46.  
  47. static void
  48. techange(tp, left, top, right, bottom)
  49.     TEXTEDIT *tp;
  50.     int left, top, right, bottom;
  51. {
  52.     if (tp->viewing) {
  53.         CLIPMIN(left, tp->vleft);
  54.         CLIPMIN(top, tp->vtop);
  55.         CLIPMAX(right, tp->vright);
  56.         CLIPMAX(bottom, tp->vbottom);
  57.     }
  58.     wchange(tp->win, left, top, right, bottom);
  59. }
  60.  
  61. static void
  62. tescroll(tp, left, top, right, bottom, dh, dv)
  63.     TEXTEDIT *tp;
  64.     int left, top, right, bottom;
  65.     int dh, dv;
  66. {
  67.     if (tp->viewing) {
  68.         CLIPMIN(left, tp->vleft);
  69.         CLIPMIN(top, tp->vtop);
  70.         CLIPMAX(right, tp->vright);
  71.         CLIPMAX(bottom, tp->vbottom);
  72.     }
  73.     wscroll(tp->win, left, top, right, bottom, dh, dv);
  74.     /* XXX Should call wchange for bits scrolled in from outside view? */
  75. }
  76.  
  77. /* Optimization for the common case insert char.
  78.    Assumes text measurement is additive. */
  79.  
  80. static void
  81. tesetoptdata(tp)
  82.     TEXTEDIT *tp;
  83. {
  84.     lineno i;
  85.     bufpos k, pos, end;
  86.     
  87.     zcheck();
  88.     zassert(tp->foclen == 0);
  89.     
  90.     pos= zaddgap(tp->foc);
  91.     tp->opt_i= i= tewhichline(tp, pos, FALSE);
  92.     tp->opt_h= tp->left + tetextwidth(tp, tp->start[i], pos);
  93.     tp->opt_v= tp->top + i*tp->vspace;
  94.     end= tp->start[i+1];
  95.     if (end > pos && zcharbefore(end) == EOL)
  96.         zdecr(&end);
  97.     while (end > pos && zcharbefore(end) == ' ')
  98.         zdecr(&end);
  99.     for (k= pos; k < end; zincr(&k)) {
  100.         if (zcharat(k) == '\t')
  101.             break;
  102.     }
  103.     if (k < end) {
  104.         tp->opt_end=
  105.             tp->left + tetextwidth(tp, tp->start[i], znext(k));
  106.         tp->opt_avail= tp->opt_end -
  107.             (tp->left + tetextwidth(tp, tp->start[i], k));
  108.     }
  109.     else {
  110.         tp->opt_end= tp->right;
  111.         tp->opt_avail= tp->width - tetextwidth(tp, tp->start[i], end);
  112.     }
  113.     if (tp->start[i] > 0 && zcharbefore(tp->start[i]) != EOL) {
  114.         tp->opt_in_first_word= TRUE;
  115.         for (k= tp->start[i]; k < pos; zincr(&k)) {
  116.             if (isspace(zcharat(k))) {
  117.                 tp->opt_in_first_word= FALSE;
  118.                 break;
  119.             }
  120.         }
  121.     }
  122.     else
  123.         tp->opt_in_first_word= FALSE;
  124.     tp->opt_valid= TRUE;
  125. }
  126.  
  127. static bool
  128. teoptinschar(tp, c)
  129.     TEXTEDIT *tp;
  130.     int c;
  131. {
  132.     int w;
  133.     
  134.     if (tp->foclen != 0 || c == EOL || c == '\t' || tp->focprev)
  135.         return FALSE;
  136.     if (!tp->opt_valid)
  137.         tesetoptdata(tp);
  138.     if (c == ' ' && tp->opt_in_first_word)
  139.         return FALSE;
  140.     w= wcharwidth(c);
  141.     if (w >= tp->opt_avail)
  142.         return FALSE;
  143.     
  144.     temovegapto(tp, tp->foc);
  145.     if (tp->gaplen < 1)
  146.         tegrowgapby(tp, 1+RESERVE);
  147.     if (tp->start[tp->opt_i] == zgapend)
  148.         tp->start[tp->opt_i]= tp->gap;
  149.     ++tp->gap;
  150.     --tp->gaplen;
  151.     tp->buf[tp->foc]= c;
  152.     ++tp->foc;
  153.     
  154.     tp->opt_avail -= w;
  155.     if (tp->active)
  156.         wnocaret(tp->win);
  157.     tescroll(tp,
  158.         tp->opt_h, tp->opt_v,
  159.         tp->opt_end, tp->opt_v + tp->vspace,
  160.         w, 0);
  161.     wbegindrawing(tp->win);
  162.     if (tp->viewing)
  163.         wcliprect(tp->vleft, tp->vtop, tp->vright, tp->vbottom);
  164.     wdrawchar(tp->opt_h, tp->opt_v, c);
  165.     wenddrawing(tp->win);
  166.     tp->opt_h += w;
  167.     if (tp->active) {
  168.         if (!tp->viewing ||
  169.             tp->vleft <= tp->opt_h &&
  170.             tp->opt_h <= tp->vright &&
  171.             tp->vtop <= tp->opt_v &&
  172.             tp->opt_v + tp->vspace <= tp->vbottom) {
  173.             wsetcaret(tp->win, tp->opt_h, tp->opt_v);
  174.             wshow(tp->win,
  175.                   tp->opt_h, tp->opt_v,
  176.                   tp->opt_h, tp->opt_v + tp->vspace);
  177.         }
  178.         else
  179.             wnocaret(tp->win);
  180.     }
  181.     tp->aim= tp->opt_h;
  182.     
  183.     return TRUE;
  184. }
  185.  
  186. static void
  187. teinsert(tp, str, len)
  188.     TEXTEDIT *tp;
  189.     char *str;
  190.     int len;
  191. {
  192.     focpos oldfoc= tp->foc;
  193.     
  194.     tehidefocus(tp);
  195.     temovegapto(tp, zfocend);
  196.     tp->gap= tp->foc;
  197.     tp->gaplen += tp->foclen;
  198.     teemptygap(tp);
  199.     tp->foclen= 0;
  200.     if (tp->gaplen < len)
  201.         tegrowgapby(tp, len-tp->gaplen+RESERVE);
  202.     strncpy(tp->buf+tp->gap, str, len);
  203.     tp->gap += len;
  204.     tp->gaplen -= len;
  205.     tp->foc += len;
  206.     terecompute(tp, zaddgap(oldfoc), zaddgap(tp->foc));
  207. }
  208.  
  209. static int lasteol;    /* Optimization trick for teendofline */
  210.  
  211. static void
  212. terecompute(tp, first, last)
  213.     TEXTEDIT *tp;
  214.     int first, last;
  215. {
  216.     lineno i;
  217.     lineno chfirst, chlast; /* Area to pass to wchange */
  218.     lineno shift= 0; /* Lines to shift down (negative: up) */
  219.     vcoord newbottom;
  220.     
  221.     tp->start[0]= zaddgap(0);
  222.     
  223.     i= 2;
  224.     while (i <= tp->nlines && tp->start[i] < first)
  225.         ++i;
  226.     i -= 2;
  227.     chfirst= tp->nlines;
  228.     chlast= i;
  229.     lasteol= -1;
  230.     
  231.     /* TO DO: scroll up/down if inserting/deleting lines */
  232.     
  233.     for (;; ++i) {
  234.         bufpos end= teendofline(tp, tp->start[i]);
  235.         bool unchanged= (i < tp->nlines && end == tp->start[i+1]);
  236.         if (!unchanged)
  237.             shift += tesetstart(tp, i+1, end, last);
  238.         if (!(unchanged && end < first)) {
  239.             if (i < chfirst)
  240.                 chfirst= i;
  241.             chlast= i+1;
  242.         }
  243.         if (end >= tp->buflen) {
  244.             if (end > tp->start[i] && zcharbefore(end) == EOL)
  245.                 continue;
  246.             else
  247.                 break;
  248.         }
  249.         if (unchanged && end > last) {
  250.             i= tp->nlines-1;
  251.             break;
  252.         }
  253.     }
  254.     
  255.     zassert(tp->nlines > i);
  256.     
  257.     if (tp->drawing) {
  258.         if (shift != 0) {
  259.             lineno k= chlast;
  260.             if (shift > 0)
  261.                 k -= shift;
  262.             tescroll(tp,
  263.                 tp->left, tp->top + k*tp->vspace,
  264.                 tp->right, tp->top + tp->nlines*tp->vspace,
  265.                 0, shift*tp->vspace);
  266.         }
  267.         
  268.         techange(tp,
  269.             tp->left, tp->top + chfirst*tp->vspace,
  270.             tp->right, tp->top + chlast*tp->vspace);
  271.     }
  272.     
  273.     tp->nlines= i+1;
  274.     newbottom= tp->top + tp->vspace*tp->nlines;
  275.     if (newbottom < tp->bottom)
  276.         techange(tp,
  277.             tp->left, newbottom, tp->right, tp->bottom);
  278.     tp->bottom= newbottom;
  279.     tp->aim= UNDEF;
  280.     tp->focprev= FALSE;
  281.     if (tp->drawing)
  282.         tesetcaret(tp);
  283.     
  284.     zcheck();
  285. }
  286.  
  287. static int
  288. tesetstart(tp, i, pos, last)
  289.     register TEXTEDIT *tp;
  290.     register lineno i;
  291.     bufpos pos, last;
  292. {
  293.     if (i > tp->nlines) {
  294.         tp->nlines= i;
  295.         if (tp->nlines >= tp->nstart) {
  296.             tp->nstart= tp->nlines + STARTINCR;
  297.             tp->start= (bufpos*)zrealloc((char*)tp->start,
  298.                 tp->nstart*sizeof(int));
  299.         }
  300.         tp->start[i]= pos;
  301.         return 0;
  302.     }
  303.     else {
  304.         lineno shift= 0;
  305.         lineno k;
  306.         for (k= i; k < tp->nlines; ++k) {
  307.             if (tp->start[k] > pos)
  308.                 break;
  309.         }
  310.         shift= k-1 - i;
  311.         /* start[k] should really be start[i+1] */
  312.         if (shift < 0 && tp->start[k] >= last) { /* Insert one */
  313.             ++tp->nlines;
  314.             if (tp->nlines >= tp->nstart) {
  315.                 tp->nstart= tp->nlines + STARTINCR;
  316.                 tp->start= (int*)zrealloc((char*)tp->start,
  317.                     tp->nstart*sizeof(int));
  318.             }
  319.             for (k= tp->nlines; k > i; --k)
  320.                 tp->start[k]= tp->start[k-1];
  321.         }
  322.         else if (shift > 0 && pos >= last) { /* Delete some */
  323.             for (; k <= tp->nlines; ++k)
  324.                 tp->start[k-shift]= tp->start[k];
  325.             tp->nlines -= shift;
  326.             if (tp->nlines < tp->nstart - STARTINCR) {
  327.                 tp->nstart= tp->nlines+1;
  328.                 tp->start= (int*)zrealloc((char*)tp->start,
  329.                     tp->nstart*sizeof(int));
  330.             }
  331.         }
  332.         else
  333.             shift= 0; /* Don't shift (yet) */
  334.         tp->start[i]= pos;
  335.         return -shift;
  336.     }
  337. }
  338.  
  339. static int
  340. teendofline(tp, pos)
  341.     TEXTEDIT *tp;
  342.     bufpos pos;
  343. {
  344.     bufpos end= tp->buflen;
  345.     bufpos k;
  346.     
  347.     /* Find first EOL if any */
  348.     if (lasteol >= pos)
  349.         k= lasteol;
  350.     else {
  351.         for (k= pos; k < end && zcharat(k) != EOL; zincr(&k))
  352.             ;
  353.         lasteol= k;
  354.     }
  355.     
  356.     end= tetextbreak(tp, pos, k, tp->width);
  357.     
  358.     /* Extend with any spaces immediately following end */
  359.     for (; end < tp->buflen && zcharat(end) == ' '; zincr(&end))
  360.         ;
  361.     
  362.     if (end < tp->buflen) {
  363.         /* Extend with immediately following EOL */
  364.         if (zcharat(end) == EOL)
  365.             zincr(&end);
  366.         else {
  367.             /* Search back for space before last word */
  368.             for (k= end; zdecr(&k) >= pos && !isspace(zcharat(k)); )
  369.                 ;
  370.             
  371.             if (k >= pos)
  372.                 end= znext(k);
  373.         }
  374.     }
  375.  
  376.     /* Each line must be at least one character long,
  377.        otherwise a very narrow text-edit box would cause
  378.        the size calculation to last forever */
  379.     if (end == pos && end < tp->buflen)
  380.         zincr(&end);
  381.     
  382.     return end;
  383. }
  384.  
  385. bool
  386. teevent(tp, e)
  387.     TEXTEDIT *tp;
  388.     EVENT *e;
  389. {
  390.     if (e->window != tp->win)
  391.         return FALSE;
  392.     
  393.     switch (e->type) {
  394.     
  395.     case WE_CHAR:
  396.         teinschar(tp, e->u.character);
  397.         break;
  398.     
  399.     case WE_COMMAND:
  400.         switch (e->u.command) {
  401.         case WC_BACKSPACE:
  402.             tebackspace(tp);
  403.             break;
  404.         case WC_RETURN:
  405.             teinschar(tp, EOL);
  406.             break;
  407.         case WC_TAB:
  408.             teinschar(tp, '\t');
  409.             break;
  410.         case WC_LEFT:
  411.         case WC_RIGHT:
  412.         case WC_UP:
  413.         case WC_DOWN:
  414.             tearrow(tp, e->u.command);
  415.             break;
  416.         default:
  417.             return FALSE;
  418.         }
  419.         break;
  420.     
  421.     case WE_MOUSE_DOWN:
  422.         {
  423.             int h= e->u.where.h, v= e->u.where.v;
  424.             if (tp->viewing) {
  425.                 if (h < tp->vleft || h > tp->vright ||
  426.                     v < tp->vtop || v > tp->vbottom)
  427.                     return FALSE;
  428.             }
  429.             else {
  430.                 if (h < tp->left || h > tp->right ||
  431.                     v < tp->top || v > tp->bottom)
  432.                     return FALSE;
  433.             }
  434.             teclicknew(tp, h, v,
  435.                    e->u.where.button == 3 ||
  436.                    (e->u.where.mask & WM_SHIFT),
  437.                    e->u.where.clicks > 1);
  438.         }
  439.         break;
  440.     
  441.     case WE_MOUSE_MOVE:
  442.     case WE_MOUSE_UP:
  443.         if (!tp->mdown)
  444.             return FALSE;
  445.         teclicknew(tp, e->u.where.h, e->u.where.v, TRUE, tp->dclick);
  446.         if (e->type == WE_MOUSE_UP)
  447.             tp->mdown= FALSE;
  448.         break;
  449.     
  450.     case WE_DRAW:
  451.         wbegindrawing(tp->win);
  452.         tedrawnew(tp, e->u.area.left, e->u.area.top,
  453.                 e->u.area.right, e->u.area.bottom);
  454.         wenddrawing(tp->win);
  455.         break;
  456.     
  457.     default:
  458.         return FALSE;
  459.     
  460.     }
  461.     
  462.     /* If broke out of switch: */
  463.     return TRUE;
  464. }
  465.  
  466. void
  467. tearrow(tp, code)
  468.     TEXTEDIT *tp;
  469.     int code;
  470. {
  471.     lineno i;
  472.     bufpos pos;
  473.     
  474.     tehidefocus(tp);
  475.     
  476.     switch (code) {
  477.     
  478.     case WC_LEFT:
  479.         if (tp->foclen != 0)
  480.             tp->foclen= 0;
  481.         else {
  482.             if (tp->foc > 0)
  483.                 --tp->foc;
  484.             else
  485.                 wfleep();
  486.         }
  487.         tp->aim= UNDEF;
  488.         tp->focprev= FALSE;
  489.         break;
  490.     
  491.     case WC_RIGHT:
  492.         if (tp->foclen != 0) {
  493.             tp->foc += tp->foclen;
  494.             tp->foclen= 0;
  495.         }
  496.         else {
  497.             if (tp->foc < tp->buflen-tp->gaplen)
  498.                 ++tp->foc;
  499.             else
  500.                 wfleep();
  501.         }
  502.         tp->aim= UNDEF;
  503.         tp->focprev= FALSE;
  504.         break;
  505.     
  506.     /* TO DO: merge the following two cases */
  507.     
  508.     case WC_UP:
  509.         if (tp->foclen > 0)
  510.             tp->foclen= 0;
  511.         else {
  512.             pos= zaddgap(tp->foc);
  513.             i= tewhichline(tp, pos, (bool) tp->focprev);
  514.             if (i <= 0)
  515.                 wfleep();
  516.             else {
  517.                 if (tp->aim == UNDEF)
  518.                     tp->aim= tp->left + tetextwidth(tp,
  519.                         tp->start[i], pos);
  520.                 --i;
  521.                 pos= tetextround(tp, i, tp->aim);
  522.                 tp->foc= zsubgap(pos);
  523.                 tp->focprev= (pos == tp->start[i+1]);
  524.             }
  525.         }
  526.         break;
  527.     
  528.     case WC_DOWN:
  529.         if (tp->foclen > 0) {
  530.             tp->foc += tp->foclen;
  531.             tp->foclen= 0;
  532.         }
  533.         else {
  534.             pos= zaddgap(tp->foc);
  535.             i= tewhichline(tp, pos, (bool) tp->focprev);
  536.             if (i+1 >= tp->nlines)
  537.                 wfleep();
  538.             else {
  539.                 if (tp->aim == UNDEF)
  540.                     tp->aim= tp->left + tetextwidth(tp,
  541.                         tp->start[i], pos);
  542.                 ++i;
  543.                 pos= tetextround(tp, i, tp->aim);
  544.                 tp->foc= zsubgap(pos);
  545.                 tp->focprev= (pos == tp->start[i+1]);
  546.             }
  547.         }
  548.         break;
  549.     
  550.     default:
  551.         dprintf("tearrow: bad code %d", code);
  552.         break;
  553.         
  554.     }
  555.     tesetcaret(tp);
  556. }
  557.  
  558. void
  559. tebackspace(tp)
  560.     TEXTEDIT *tp;
  561. {
  562.     if (tp->foclen == 0) {
  563.         if (tp->foc == 0) {
  564.             wfleep();
  565.             return;
  566.         }
  567.         --tp->foc;
  568.         tp->foclen= 1;
  569.     }
  570.     teinsert(tp, "", 0);
  571. }
  572.  
  573. bool
  574. teclicknew(tp, h, v, extend, dclick)
  575.     TEXTEDIT *tp;
  576.     coord h, v;
  577.     bool extend, dclick;
  578. {
  579.     lineno i;
  580.     bufpos pos;
  581.     focpos f;
  582.     
  583.     tp->dclick= dclick;
  584.     pos= tewhereis(tp, h, v, &i);
  585.     f= zsubgap(pos);
  586.     if (extend) {
  587.         if (!tp->mdown) {
  588.             tp->mdown= TRUE;
  589.             if (f - tp->foc < tp->foc + tp->foclen - f)
  590.                 tp->anchor= tp->foc + tp->foclen;
  591.             else
  592.                 tp->anchor= tp->foc;
  593.             tp->anchor2= tp->anchor;
  594.         }
  595.         if (f >= tp->anchor) {
  596.             if (dclick)
  597.                 f= tewordend(tp, f);
  598.             techangefocus(tp, tp->anchor, f);
  599.         }
  600.         else {
  601.             if (dclick)
  602.                 f= tewordbegin(tp, f);
  603.             techangefocus(tp, f, tp->anchor2);
  604.         }
  605.     }
  606.     else {
  607.         tp->mdown= TRUE;
  608.         tp->anchor= tp->anchor2= f;
  609.         if (dclick) {
  610.             tp->anchor= tewordbegin(tp, tp->anchor);
  611.             tp->anchor2= tewordend(tp, f);
  612.         }
  613.         techangefocus(tp, tp->anchor, tp->anchor2);
  614.     }
  615.     tp->aim= UNDEF;
  616.     tp->focprev= (tp->foclen == 0 && pos == tp->start[i+1]);
  617.     tesetcaret(tp);
  618.     return TRUE;
  619. }
  620.  
  621. /* Return f, 'rounded down' to a word begin */
  622.  
  623. static int
  624. tewordbegin(tp, f)
  625.     TEXTEDIT *tp;
  626.     int f;
  627. {
  628.     f= zaddgap(f);
  629.     for (;;) {
  630.         if (f == 0 || isspace(zcharbefore(f)))
  631.             break;
  632.         zdecr(&f);
  633.     }
  634.     return zsubgap(f);
  635. }
  636.  
  637. /* Ditto to word end */
  638.  
  639. static int
  640. tewordend(tp, f)
  641.     TEXTEDIT *tp;
  642.     int f;
  643. {
  644.     f= zaddgap(f);
  645.     for (;;) {
  646.         if (f >= tp->buflen || isspace(zcharat(f)))
  647.             break;
  648.         zincr(&f);
  649.     }
  650.     return zsubgap(f);
  651. }
  652.  
  653. int
  654. tegetleft(tp)
  655.     TEXTEDIT *tp;
  656. {
  657.     return tp->left;
  658. }
  659.  
  660. int
  661. tegettop(tp)
  662.     TEXTEDIT *tp;
  663. {
  664.     return tp->top;
  665. }
  666.  
  667. int
  668. tegetright(tp)
  669.     TEXTEDIT *tp;
  670. {
  671.     return tp->right;
  672. }
  673.  
  674. int
  675. tegetbottom(tp)
  676.     TEXTEDIT *tp;
  677. {
  678.     return tp->bottom;
  679. }
  680.  
  681. void
  682. temove(tp, left, top, width)
  683.     TEXTEDIT *tp;
  684.     coord left, top, width;
  685. {
  686.     temovenew(tp, left, top, left+width, top + tp->nlines*tp->vspace);
  687. }
  688.  
  689. /*ARGSUSED*/
  690. void
  691. temovenew(tp, left, top, right, bottom)
  692.     TEXTEDIT *tp;
  693.     int left, top, right, bottom;
  694. {
  695.     int oldheight= tp->bottom - tp->top;
  696.     tp->left= left;
  697.     tp->top= top;
  698.     tp->right= right;
  699.     tp->bottom= tp->top + oldheight;
  700.     if (right - left != tp->width) {
  701.         tp->width= right - left;
  702.         tp->nlines= 0; /* Experimental! */
  703.         terecompute(tp, 0, tp->buflen);
  704.     }
  705. }
  706.  
  707. void
  708. tesetview(tp, left, top, right, bottom)
  709.     TEXTEDIT *tp;
  710.     int left, top, right, bottom;
  711. {
  712.     tp->viewing= TRUE;
  713.     tp->vleft= left;
  714.     tp->vtop= top;
  715.     tp->vright= right;
  716.     tp->vbottom= bottom;
  717. }
  718.  
  719. void
  720. tenoview(tp)
  721.     TEXTEDIT *tp;
  722. {
  723.     tp->viewing= FALSE;
  724. }
  725.  
  726. void
  727. tesetfocus(tp, foc1, foc2)
  728.     TEXTEDIT *tp;
  729.     focpos foc1, foc2;
  730. {
  731.     if (foc1 > tp->buflen - tp->gaplen)
  732.         foc1= tp->buflen - tp->gaplen;
  733.     if (foc2 > tp->buflen - tp->gaplen)
  734.         foc2= tp->buflen - tp->gaplen;
  735.     if (foc1 < 0)
  736.         foc1= 0;
  737.     if (foc2 < foc1)
  738.         foc2= foc1;
  739.     techangefocus(tp, foc1, foc2);
  740.     tp->aim= UNDEF;
  741.     tp->focprev= FALSE;
  742.     tesetcaret(tp);
  743. }
  744.  
  745. int
  746. tegetfoc1(tp)
  747.     TEXTEDIT *tp;
  748. {
  749.     return tp->foc;
  750. }
  751.  
  752. int
  753. tegetfoc2(tp)
  754.     TEXTEDIT *tp;
  755. {
  756.     return tp->foc + tp->foclen;
  757. }
  758.  
  759. int
  760. tegetnlines(tp)
  761.     TEXTEDIT *tp;
  762. {
  763.     return tp->nlines;
  764. }
  765.  
  766. char *
  767. tegettext(tp)
  768.     TEXTEDIT *tp;
  769. {
  770.     temovegapto(tp, tp->buflen - tp->gaplen);
  771.     if (tp->gaplen < 1)
  772.         tegrowgapby(tp, 1+RESERVE);
  773.     tp->buf[tp->gap]= EOS;
  774.     return tp->buf;
  775. }
  776.  
  777. int
  778. tegetlen(tp)
  779.     TEXTEDIT *tp;
  780. {
  781.     return tp->buflen - tp->gaplen;
  782. }
  783.  
  784. void
  785. tesetbuf(tp, buf, buflen)
  786.     TEXTEDIT *tp;
  787.     char *buf;
  788.     int buflen;
  789. {
  790.     bool drawing= tp->drawing;
  791.     
  792.     if (buf == NULL || buflen < 0)
  793.         return;
  794.     if (drawing)
  795.         techange(tp, tp->left, tp->top, tp->right, tp->bottom);
  796.     free(tp->buf);
  797.     tp->buf= buf;
  798.     tp->buflen= buflen;
  799.     tp->foc= tp->foclen= tp->gap= tp->gaplen= 0;
  800.     tp->drawing= FALSE;
  801.     terecompute(tp, 0, tp->buflen);
  802.     if (drawing) {
  803.         tp->drawing= TRUE;
  804.         techange(tp, tp->left, tp->top, tp->right, tp->bottom);
  805.     }
  806. }
  807.  
  808.  
  809. /* The following paragraph-drawing routines are experimental.
  810.    They cannibalize on the existing text-edit code,
  811.    which makes it trivial to ensure the lay-out is the same,
  812.    but causes overhead to initialize a text-edit struct.
  813.    The flag 'drawing' has been added to the textedit struct,
  814.    which suppresses calls to tesetcaret, wchange and wscroll
  815.    from tesetup and terecompute.
  816.    It doesn't suppress actual drawing, which only occurs when
  817.    tedraw is called.
  818.    Note -- this could be optimized, but it is infrequently,
  819.    so I don't care until I get complaints. */
  820.  
  821. /* Draw a paragraph of text, exactly like tedraw would draw it.
  822.    Parameters are the top left corner, the width, the text and its length.
  823.    Return value is the v coordinate of the bottom line.
  824.    An empty string is drawn as one blank line. */
  825.  
  826. int
  827. wdrawpar(left, top, text, width)
  828.     int left, top;
  829.     char *text;
  830.     int width;
  831. {
  832.     return _wdrawpar(left, top, text, width, TRUE);
  833. }
  834.  
  835. /* Measure the height of a paragraph of text, when drawn with wdrawpar. */
  836.  
  837. int
  838. wparheight(text, width)
  839.     char *text;
  840.     int width;
  841. {
  842.     return _wdrawpar(0, 0, text, width, FALSE);
  843. }
  844.  
  845. /* Routine to do the dirty work for the above two.
  846.    Size calculations are implemented by going through the normal
  847.    routine but suppressing the actual drawing. */
  848.  
  849. static int
  850. _wdrawpar(left, top, text, width, draw)
  851.     int left, top;
  852.     char *text;
  853.     int width;
  854.     bool draw;
  855. {
  856.     TEXTEDIT *tp= tesetup((WINDOW*)NULL, left, top, left+width, top, FALSE);
  857.     int v;
  858.     
  859.     tesetbuf(tp, text, strlen(text));
  860.     if (draw)
  861.         tedraw(tp);
  862.     v= tegetbottom(tp);
  863.     tp->buf= NULL;
  864.     tefree(tp);
  865.     return v;
  866. }
  867.